#include "StdAfx.h"
#include "GalconSession.h"
#include "ServerMessageFactory.h"
#include <boost/make_shared.hpp>
#include <boost/bind.hpp>

GalconSession::GalconSession(boost::asio::io_service& service, GalconRoom& room)
                            : m_socket(service)
                            , m_room(room)
{
}

GalconSession::~GalconSession()
{
}


void GalconSession::Deliver(const Message &msg)
{
   traceMessage(msg, "GalconSession::Deliver");
   bool write_in_progress = !m_messageDeque.empty();
   m_messageDeque.push_back(msg);
   if (!write_in_progress)
   {
      boost::asio::async_write(m_socket,
         boost::asio::buffer(m_messageDeque.front().GetData(), m_messageDeque.front().GetLength()),
         boost::bind(&GalconSession::handle_write, shared_from_this(),
         boost::asio::placeholders::error));
   }
}

void GalconSession::handle_read_body(const boost::system::error_code &error)
{
   if (!error)
   {
      traceMessage(m_message, "GalconSession::handle_read_body");
      
      MessageBasePtr message = m_messageFactory->ParseMessage(m_message);
      message->Execute();
      boost::asio::async_read(m_socket,
         boost::asio::buffer(m_message.GetData(), Message::header_length),
         boost::bind(&GalconSession::handle_read_header, shared_from_this(),
         boost::asio::placeholders::error));
   }
   else
   {
      std::cout << "error occurred in GalconSession::handle_read_body" << error.message();
      m_room.Leave(shared_from_this());
   }
}

void GalconSession::handle_read_header(const boost::system::error_code &error)
{
   if (!error && m_message.DecodeHeader())
   {
      boost::asio::async_read(m_socket,
         boost::asio::buffer(m_message.GetBody(), m_message.GetBodyLength()),
         boost::bind(&GalconSession::handle_read_body, shared_from_this(),
         boost::asio::placeholders::error));
   }
   else
   {
      std::cout << "error occurred in GalconSession::handle_read_header" << error.message();
      m_room.Leave(shared_from_this());
   }
}

void GalconSession::handle_write(const boost::system::error_code &error)
{
   if (!error)
   {
      m_messageDeque.pop_front();
      if (!m_messageDeque.empty())
      {
        boost::asio::async_write(m_socket,
           boost::asio::buffer(m_messageDeque.front().GetData(),
           m_messageDeque.front().GetLength()),
           boost::bind(&GalconSession::handle_write, shared_from_this(),
              boost::asio::placeholders::error));
      }
   }
   else
   {
      std::cout << "error occurred in GalconSession::handle_write" << error.message();
      m_room.Leave(shared_from_this());
   }
}

void GalconSession::Start()
{
   m_messageFactory = boost::make_shared<ServerMessageFactory>(boost::ref(m_room), shared_from_this());
   m_room.Join(shared_from_this());
   boost::asio::async_read(m_socket,
      boost::asio::buffer(m_message.GetData(), Message::header_length),
      boost::bind(&GalconSession::handle_read_header, shared_from_this(),
          boost::asio::placeholders::error));
}

void GalconSession::traceMessage( const Message& msg, const std::string& info)
{
   std::string m(msg.GetBody(), msg.GetBodyLength());
   std::cout << info << m << '\n';
}